package sdk

import (
	"chainmaker/internal/buffer"
	"chainmaker/internal/mock"
	"chainmaker/logger"
	"chainmaker/pb/protogo"
	"encoding/hex"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"chainmaker.org/chainmaker/common/v2/bytehelper"
	"chainmaker.org/chainmaker/common/v2/serialize"
	"chainmaker.org/chainmaker/pb-go/v2/syscontract"
	vmPb "chainmaker.org/chainmaker/pb-go/v2/vm"
	"chainmaker.org/chainmaker/protocol/v2"
	"go.uber.org/zap"
	"golang.org/x/crypto/sha3"
)

// check interface implement
var _ SDKInterface = (*SDK)(nil)

type Bool int32

type ECKeyType = string

type SDK struct {
	args map[string][]byte

	crossCtx *protogo.CrossContext
	// sdk向RuntimeClient发送消息的方法
	sendSysCallRequestWithRespNotify func(msg *protogo.DockerVMMessage, responseNotify func(message *protogo.DockerVMMessage))

	// cache
	readMap  map[string][]byte
	writeMap map[string][]byte
	// contract parameters
	creatorOrgId  string
	creatorRole   string
	creatorPk     string
	senderOrgId   string
	senderRole    string
	senderPk      string
	blockHeight   string
	txId          string
	originalTxId  string
	chainId       string
	txTimeStamp   string
	senderAddr    string // 跨合约调用的时候作为调用者的地址, __cross_caller__
	senderAddress string // 作为交易的最初发起者的地址,  __sender_addr__
	origin        string //
	// events
	contractName string
	events       []*protogo.Event
	// logger
	contractLogger *zap.SugaredLogger
	sandboxLogger  *zap.SugaredLogger

	//start extra for mock contract invoke
	logLevel        string
	StateMap        *mock.GlobalStateStore
	proxyContract   Contract
	depth           int
	addressToNameMp map[string]string
}

func NewSDK(
	crossCtx *protogo.CrossContext,
	sendFunc func(msg *protogo.DockerVMMessage, responseNotify func(msg *protogo.DockerVMMessage)),
	txId string,
	originalTxId string,
	chainId string,
	contractName string,
	contractLogger *zap.SugaredLogger,
	SandboxLogger *zap.SugaredLogger,
	args map[string][]byte,
) *SDK {
	var events []*protogo.Event

	s := &SDK{
		crossCtx:                         crossCtx,
		sendSysCallRequestWithRespNotify: sendFunc,

		originalTxId:   originalTxId,
		chainId:        chainId,
		args:           args,
		readMap:        make(map[string][]byte, MapSize),
		writeMap:       make(map[string][]byte, MapSize),
		creatorOrgId:   initStubContractParam(args, ContractParamCreatorOrgId, ContractParamCreatorOrgIdValue),
		creatorRole:    initStubContractParam(args, ContractParamCreatorRole, ContractParamCreatorRoleValue),
		creatorPk:      initStubContractParam(args, ContractParamCreatorPk, ContractParamCreatorPkValue),
		senderOrgId:    initStubContractParam(args, ContractParamSenderOrgId, ContractParamSenderOrgIdValue),
		senderRole:     initStubContractParam(args, ContractParamSenderRole, ContractParamSenderRoleValue),
		senderPk:       initStubContractParam(args, ContractParamSenderPk, ContractParamSenderPkValue),
		senderAddress:  initStubContractParam(args, ContractParamCrossCaller, ContractParamSenderPkValue),
		blockHeight:    initStubContractParam(args, ContractParamBlockHeight, ContractParamBlockHeightValue),
		txId:           initStubContractParam(args, ContractParamTxId, ContractParamTxIdValue),
		txTimeStamp:    initStubContractParam(args, ContractParamTxTimeStamp, ContractParamTxTimeStampValue),
		senderAddr:     initStubContractParam(args, ContractSenderAddr, ContractSenderAddrValue),
		contractLogger: contractLogger,
		sandboxLogger:  SandboxLogger,
		events:         events,
		contractName:   contractName,
	}

	return s
}

func InitSDKMock(
	contractName string,
	logger *zap.SugaredLogger,
	args map[string][]byte,
	globalStateStore *mock.GlobalStateStore,
	proxyContract Contract,
	depth int,
	addressToNameMp map[string]string,
) {
	ts := strconv.FormatInt(time.Now().Unix(), 10)
	generateTxId := getTimestampTxId()
	var events []*protogo.Event
	s := &SDK{
		chainId:        ContractParamChainIdValue,
		args:           args,
		readMap:        make(map[string][]byte, MapSize),
		writeMap:       make(map[string][]byte, MapSize),
		creatorOrgId:   initStubContractParam(args, ContractParamCreatorOrgId, ContractParamCreatorOrgIdValue),
		creatorRole:    initStubContractParam(args, ContractParamCreatorRole, ContractParamCreatorRoleValue),
		creatorPk:      initStubContractParam(args, ContractParamCreatorPk, ContractParamCreatorPkValue),
		senderOrgId:    initStubContractParam(args, ContractParamSenderOrgId, ContractParamSenderOrgIdValue),
		senderRole:     initStubContractParam(args, ContractParamSenderRole, ContractParamSenderRoleValue),
		senderPk:       initStubContractParam(args, ContractParamSenderPk, ContractParamSenderPkValue),
		senderAddress:  initStubContractParam(args, ContractSenderAddr, ContractSenderAddrValue),
		blockHeight:    initStubContractParam(args, ContractParamBlockHeight, ContractParamBlockHeightValue),
		txId:           initStubContractParam(args, ContractParamTxId, generateTxId),
		txTimeStamp:    initStubContractParam(args, ContractParamTxTimeStamp, ts),
		senderAddr:     initStubContractParam(args, ContractParamCrossCaller, ContractSenderAddrValue),
		originalTxId:   initStubContractParam(args, ContractParamTxId, generateTxId),
		contractLogger: logger,
		sandboxLogger:  logger,
		events:         events,
		contractName:   contractName,

		StateMap:        globalStateStore,
		proxyContract:   proxyContract,
		depth:           depth,
		addressToNameMp: addressToNameMp,
	}
	Instance = s
	if Bulletproofs != nil {
		Bulletproofs = NewBulletproofsInstance()
	}
	if Paillier != nil {
		Paillier = NewPaillierInstance()
	}
}

func initStubContractParam(args map[string][]byte, key string, defaultValue string) string {
	if value, ok := args[key]; ok {
		delete(args, key)
		return string(value)
	} else {
		return defaultValue
	}
}

//func initStubContractParam(args map[string][]byte, key string) string {
//	if value, ok := args[key]; ok {
//		delete(args, key)
//		return string(value)
//	} else {
//		//s.sandboxLogger.Errorf("init contract parameter [%v] failed", key)
//		return ""
//	}
//}

func (s *SDK) GetArgs() map[string][]byte {
	return s.args
}

func (s *SDK) GetContractName() string {
	return s.contractName
}
func (s *SDK) GetStateMap() *mock.GlobalStateStore {
	return s.StateMap
}
func (s *SDK) GetDepth() int {
	return s.depth
}

func (s *SDK) WithGlobalState(stateMap *mock.GlobalStateStore) {
	s.StateMap = stateMap
}

func (s *SDK) WithProxyContract(proxyContract Contract) {
	s.proxyContract = proxyContract
}

func (s *SDK) GetState(key, field string) (string, error) {
	// get from write set
	if value, err := s.getFromStateMap(key, field); err != nil {
		return "", err
	} else {
		return value, nil
	}
}

func (s *SDK) GetStateWithExists(key, field string) (string, bool, error) {
	value, err := s.getFromStateMap(key, field)
	if err != nil {
		return "", false, err
	}
	if value == "" {
		return "", false, nil
	}
	return value, true, nil
}

func (s *SDK) GetStateFromKeyWithExists(key string) (string, bool, error) {
	return s.GetStateWithExists(key, "")
}

func (s *SDK) GetStateByte(key, field string) ([]byte, error) {
	// get from write set
	if value, err := s.getFromStateMap(key, field); err != nil {
		return nil, err
	} else {
		return []byte(value), nil
	}
}

func (s *SDK) GetStateFromKey(key string) (string, error) {
	return s.GetState(key, "")
}

func (s *SDK) GetStateFromKeyByte(key string) ([]byte, error) {
	return s.GetStateByte(key, "")
}

func (s *SDK) PutState(key, field string, value string) error {
	return s.putIntoStateMap(key, field, []byte(value))
}

func (s *SDK) PutStateByte(key, field string, value []byte) error {
	return s.putIntoStateMap(key, field, value)
}

func (s *SDK) PutStateFromKey(key string, value string) error {
	return s.putIntoStateMap(key, "", []byte(value))
}

func (s *SDK) PutStateFromKeyByte(key string, value []byte) error {
	return s.putIntoStateMap(key, "", value)
}

func (s *SDK) DelState(key, field string) error {
	return s.putIntoStateMap(key, field, nil)
}

func (s *SDK) DelStateFromKey(key string) error {
	return s.putIntoStateMap(key, "", nil)
}

func (s *SDK) getFromStateMap(key, field string) (string, error) {
	contractKey := s.constructKey(key, field)
	s.sandboxLogger.Debugf("get key[%s] from write set\n", contractKey)
	value, err := s.StateMap.Get(contractKey)
	if err != nil {
		return "", err
	}
	return value, nil
}

func (s *SDK) putIntoStateMap(key, field string, value []byte) error {
	if err := protocol.CheckKeyFieldStr(key, field); err != nil {
		return err
	}
	contractKey := s.constructKey(key, field)
	s.sandboxLogger.Debugf("put key[%s] - value[%s] into write set\n", contractKey, string(value))
	s.StateMap.Put(contractKey, string(value))
	return nil
}

func (s *SDK) GetBatchState(batchKeys []*vmPb.BatchKey) ([]*vmPb.BatchKey, error) {
	s.sandboxLogger.Debugf("get batch state for keys: %v", batchKeys)
	var (
		done bool

		writeSetValues    []*vmPb.BatchKey
		readSetValues     []*vmPb.BatchKey
		emptyWriteSetKeys []*vmPb.BatchKey
		emptyReadSetKeys  []*vmPb.BatchKey
	)

	if err := s.batchKeysLimit(batchKeys); err != nil {
		return nil, err
	}

	for _, key := range batchKeys {
		if err := protocol.CheckKeyFieldStr(key.Key, key.Field); err != nil {
			return nil, err
		}
	}

	// batch get from write set
	if writeSetValues, emptyWriteSetKeys, done = s.batchGetFromWriteSet(batchKeys); done {
		return writeSetValues, nil
	}

	// batch get read set
	if readSetValues, emptyReadSetKeys, done = s.batchGetFromReadSet(emptyWriteSetKeys); done {
		return append(writeSetValues, readSetValues...), nil
	}

	// get batch state
	value, err := s.getBatchState(emptyReadSetKeys)
	if err != nil {
		return nil, err
	}

	s.sandboxLogger.Debugf("get batch state finished for batch keys %v, values: %v", batchKeys, value)

	return append(append(writeSetValues, readSetValues...), value...), nil
}

func (s *SDK) getBatchState(batchKeys []*vmPb.BatchKey) ([]*vmPb.BatchKey, error) {
	var retKeys []*vmPb.BatchKey
	for i := 0; i < len(batchKeys); i++ {
		tempV, tempVErr := s.GetStateByte(batchKeys[i].Key, batchKeys[i].Field)
		if tempVErr != nil {
			s.sandboxLogger.Warnf("getBatchState key [%s],field [%s], error [%s]",
				batchKeys[i].Key, batchKeys[i].Field, tempVErr.Error())
			continue
		}
		tempKey := &vmPb.BatchKey{
			Key:          batchKeys[i].Key,
			Field:        batchKeys[i].Field,
			Value:        tempV,
			ContractName: batchKeys[i].ContractName,
		}
		retKeys = append(retKeys, tempKey)
	}
	return retKeys, nil
}

func (s *SDK) batchGetFromWriteSet(batchKeys []*vmPb.BatchKey) ([]*vmPb.BatchKey, []*vmPb.BatchKey, bool) {
	txWrites := make([]*vmPb.BatchKey, 0, len(batchKeys))
	emptyTxWritesKeys := make([]*vmPb.BatchKey, 0, len(batchKeys))
	for _, k := range batchKeys {
		contractKey := s.constructKey(k.Key, k.Field)
		s.sandboxLogger.Debugf("batch get key[%s] from write set\n", contractKey)
		txWrite, ok := s.writeMap[contractKey]
		if !ok {
			emptyTxWritesKeys = append(emptyTxWritesKeys, k)
		} else {
			k.Value = txWrite
			txWrites = append(txWrites, k)
			s.putIntoReadSet(k.Key, k.Field, txWrite)
		}
	}

	if len(emptyTxWritesKeys) == 0 {
		return txWrites, nil, true
	}

	return txWrites, emptyTxWritesKeys, false
}

func (s *SDK) batchGetFromReadSet(batchKeys []*vmPb.BatchKey) ([]*vmPb.BatchKey, []*vmPb.BatchKey, bool) {
	txReads := make([]*vmPb.BatchKey, 0, len(batchKeys))
	emptyTxReadsKeys := make([]*vmPb.BatchKey, 0, len(batchKeys))
	for _, k := range batchKeys {
		contractKey := s.constructKey(k.Key, k.Field)
		s.sandboxLogger.Debugf("batch get key[%s] from read set\n", contractKey)
		txRead, ok := s.readMap[contractKey]
		if !ok {
			k.ContractName = s.contractName
			emptyTxReadsKeys = append(emptyTxReadsKeys, k)
		} else {
			k.Value = txRead
			txReads = append(txReads, k)
		}
	}

	if len(emptyTxReadsKeys) == 0 {
		return txReads, nil, true
	}

	return txReads, emptyTxReadsKeys, false
}

func (s *SDK) putIntoWriteSet(key, field string, value []byte) {
	contractKey := s.constructKey(key, field)
	s.writeMap[contractKey] = value
	s.sandboxLogger.Debugf("put key[%s] - value[%s] into write set\n", contractKey, fmt.Sprintf("%.100q", string(value)))
}

func (s *SDK) putIntoReadSet(key, field string, value []byte) {
	contractKey := s.constructKey(key, field)
	s.readMap[contractKey] = value
	s.sandboxLogger.Debugf("put key[%s] - value[%s] into read set\n", key, fmt.Sprintf("%.100q", string(value)))
}

func (s *SDK) batchPutIntoReadSet(batchKeys []*vmPb.BatchKey) {
	for _, batchKey := range batchKeys {
		contractKey := s.constructKey(batchKey.Key, batchKey.Field)
		s.readMap[contractKey] = batchKey.Value
		s.sandboxLogger.Debugf("batch put key[%s] - value[%s] into read set\n", batchKey.Key, batchKey.Value)
	}
}

func (s *SDK) convertByteSliceToStringSlice(value [][]byte) []string {
	stringSlice := make([]string, len(value))
	for _, bytes := range value {
		stringSlice = append(stringSlice, string(bytes))
	}
	return stringSlice
}

func (s *SDK) constructKey(key, field string) string {
	var builder strings.Builder
	builder.WriteString(s.contractName)
	builder.WriteString(sandboxKVStoreSeparator)
	builder.WriteString(key)

	if len(field) > 0 {
		builder.WriteString(sandboxKVStoreSeparator)
		builder.WriteString(field)
	}

	return builder.String()
}

// DeContructKey return contractName,key,field
func (s *SDK) DeContructKey(key string) (string, string, string) {
	retS := strings.SplitN(key, sandboxKVStoreSeparator, 4)
	if len(retS) < 2 {
		return "", "", ""
	}
	if len(retS) == 2 {
		return retS[0], retS[1], ""
	}
	return retS[0], retS[1], retS[2]
}

func (s *SDK) batchKeysLimit(keys []*vmPb.BatchKey) error {
	if len(keys) > defaultLimitKeys {
		return fmt.Errorf("over batch keys count limit %d", defaultLimitKeys)
	}
	return nil
}

func (s *SDK) GetWriteMap() map[string][]byte {
	return s.writeMap
}

func (s *SDK) GetReadMap() map[string][]byte {
	return s.readMap
}

func (s *SDK) GetCreatorOrgId() (string, error) {
	if len(s.creatorOrgId) == 0 {
		return s.creatorOrgId, fmt.Errorf("can not get creator org id")
	} else {
		return s.creatorOrgId, nil
	}
}

func (s *SDK) GetCreatorRole() (string, error) {
	if len(s.creatorRole) == 0 {
		return s.creatorRole, fmt.Errorf("can not get creator role")
	} else {
		return s.creatorRole, nil
	}
}

func (s *SDK) GetCreatorPk() (string, error) {
	if len(s.creatorPk) == 0 {
		return s.creatorPk, fmt.Errorf("can not get creator pk")
	} else {
		return s.creatorPk, nil
	}
}

func (s *SDK) GetSenderOrgId() (string, error) {
	if len(s.senderOrgId) == 0 {
		return s.senderOrgId, fmt.Errorf("can not get sender org id")
	} else {
		return s.senderOrgId, nil
	}
}

func (s *SDK) GetSenderRole() (string, error) {
	if len(s.senderRole) == 0 {
		return s.senderRole, fmt.Errorf("can not get sender role")
	} else {
		return s.senderRole, nil
	}
}

func (s *SDK) GetSenderPk() (string, error) {
	if len(s.senderPk) == 0 {
		return s.senderPk, fmt.Errorf("can not get sender pk")
	} else {
		return s.senderPk, nil
	}
}

func (s *SDK) GetBlockHeight() (int, error) {
	if len(s.blockHeight) == 0 {
		return 0, fmt.Errorf("can not get block height")
	}
	if res, err := strconv.Atoi(s.blockHeight); err != nil {
		return 0, fmt.Errorf("block height [%v] can not convert to type int", s.blockHeight)
	} else {
		return res, nil
	}
}

func (s *SDK) GetTxId() (string, error) {
	if len(s.originalTxId) == 0 {
		return "", fmt.Errorf("can not get tx id")
	} else {
		return s.originalTxId, nil
	}
}

func (s *SDK) GetTxInfo(txId string) protogo.Response {
	//contractVersion := "v1.0.0"
	paramTxId := "txId"
	paramMethod := "method"

	contractName := syscontract.SystemContract_CHAIN_QUERY.String()
	method := syscontract.ChainQueryFunction_GET_TX_BY_TX_ID.String()
	args := map[string][]byte{
		paramTxId:   []byte(txId),
		paramMethod: []byte(method),
	}

	response := s.CallContract(contractName, method, args)
	return response
}

func (s *SDK) GetTxTimeStamp() (string, error) {
	if len(s.txTimeStamp) == 0 {
		return s.txTimeStamp, fmt.Errorf("can not get tx timestamp")
	}

	return s.txTimeStamp, nil
}

func (s *SDK) EmitEvent(topic string, data []string) {
	newEvent := &protogo.Event{
		Topic:        topic,
		ContractName: s.contractName,
		Data:         data,
	}
	s.events = append(s.events, newEvent)
}

func (s *SDK) GetEvents() []*protogo.Event {
	return s.events
}

func (s *SDK) Log(message string) {
	s.contractLogger.Debugf(message)
}

func (s *SDK) Debugf(format string, a ...interface{}) {
	s.contractLogger.Debugf(format, a...)
}

func (s *SDK) Infof(format string, a ...interface{}) {
	s.contractLogger.Infof(format, a...)
}

func (s *SDK) Warnf(format string, a ...interface{}) {
	s.contractLogger.Warnf(format, a...)
}

func (s *SDK) Errorf(format string, a ...interface{}) {
	s.contractLogger.Errorf(format, a...)
}

// func (s *SDK) Fatalf(format string, a ...interface{}) {
// 	s.contractLogger.Fatalf(format, a...)
// }

func (s *SDK) CallContract(contractName, method string, args map[string][]byte) protogo.Response {
	//s.depth++
	contractNameStr, ok := s.addressToNameMp[contractName]
	if ok {
		contractName = contractNameStr
	}
	initialArgs := map[string][]byte{
		ContractParamCreatorOrgId: []byte(s.creatorOrgId),
		ContractParamCreatorRole:  []byte(s.creatorRole),
		ContractParamCreatorPk:    []byte(s.creatorPk),
		ContractParamSenderOrgId:  []byte(s.senderOrgId),
		ContractParamSenderRole:   []byte(s.senderRole),
		ContractParamSenderPk:     []byte(s.senderPk),
		ContractParamBlockHeight:  []byte(s.blockHeight),
		ContractParamTxId:         []byte(s.txId),
		ContractParamCrossCaller:  []byte(nameToAddr(s.contractName)),
		ContractSenderAddr:        []byte(s.senderAddress),
	}

	// add user defined args
	for key, value := range args {
		initialArgs[key] = value
	}
	var contractAddress string
	for address, name := range s.addressToNameMp {
		if name == contractName {
			contractAddress = address
			break
		}
	}
	var sdkInstanceBackup = Instance

	loggerBuffer := buffer.NewLoggerBuffer()
	logger := logger.NewDockerLoggerWithWriter(fmt.Sprintf("[%s][%s]", contractAddress, contractName), "INFO", loggerBuffer.GetWriter())

	logger.Infof("CallContract %s start ", contractName)
	InitSDKMock(contractName, logger, initialArgs, s.StateMap, s.proxyContract, s.depth+1, s.addressToNameMp)
	callContractResponse := s.proxyContract.InvokeContract(method)
	calleeEvents := Instance.(*SDK).events
	Instance = sdkInstanceBackup
	s.events = append(s.events, calleeEvents...)
	lineLogMessage, err := loggerBuffer.GetLineLogMessage()
	if err != nil {
		lineLogMessage = []byte(err.Error())
	}
	s.contractLogger.Infof(string(lineLogMessage))
	return callContractResponse
}

func (s *SDK) NewIterator(startKey string, limitKey string) (ResultSetKV, error) {
	return s.newIterator(FuncKvIteratorCreate, startKey, "", limitKey, "")
}

func (s *SDK) NewIteratorWithField(key string, startField string, limitField string) (ResultSetKV, error) {
	return s.newIterator(FuncKvIteratorCreate, key, startField, key, limitField)
}

func (s *SDK) NewIteratorPrefixWithKeyField(startKey string, startField string) (ResultSetKV, error) {
	return s.newPrefixIterator(FuncKvPreIteratorCreate, startKey, startField)
}

func (s *SDK) NewIteratorPrefixWithKey(key string) (ResultSetKV, error) {
	return s.newPrefixIterator(FuncKvPreIteratorCreate, key, "")
}

func (s *SDK) newIterator(iteratorFuncName, startKey string, startField string, limitKey string, limitField string) (
	ResultSetKV, error) {
	if err := protocol.CheckKeyFieldStr(startKey, startField); err != nil {
		return nil, err
	}
	if err := protocol.CheckKeyFieldStr(limitKey, limitField); err != nil {
		return nil, err
	}
	finalStartKey := s.constructKey(startKey, startField)
	finalLimitKey := s.constructKey(limitKey, limitField)
	storedKeys := s.StateMap.GetInRangeKey(s.contractName, finalStartKey, finalLimitKey)
	return newStateKeyResultItorator(s, storedKeys), nil

}

func (s *SDK) newPrefixIterator(iteratorFuncName, startKey string, startField string) (
	ResultSetKV, error) {
	if err := protocol.CheckKeyFieldStr(startKey, startField); err != nil {
		return nil, err
	}

	finalPrefix := s.constructKey(startKey, startField)
	storedKeys := s.StateMap.GetPrefixKey(finalPrefix)
	return newStateKeyResultItorator(s, storedKeys), nil

}

func (s *SDK) NewHistoryKvIterForKey(key, field string) (KeyHistoryKvIter, error) {
	if err := protocol.CheckKeyFieldStr(key, field); err != nil {
		return nil, err
	}

	finalPrefix := s.constructKey(key, field)
	values, valuesErr := s.StateMap.GetAllHistoryValue(finalPrefix)
	if valuesErr != nil {
		return nil, valuesErr
	}
	return newHistoryResultItorator(s, finalPrefix, values), nil
}

func (s *SDK) Origin() (string, error) {
	return s.senderAddress, nil

}
func (s *SDK) Sender() (string, error) {
	depth := s.GetDepth()
	switch depth {
	case 0:
		return s.Origin()
	default:
		return s.senderAddr, nil
	}
	// return s.senderAddr, nil
	// depth := s.GetDepth()
	// switch depth {
	// case 0, 1:
	// 	return ContractSenderAddrValue, nil
	// case 2:
	// 	return ContractSenderAddrValue1, nil
	// case 3:
	// 	return ContractSenderAddrValue2, nil
	// case 4:
	// 	return ContractSenderAddrValue3, nil
	// default:
	// 	return ContractSenderAddrValue4, nil
	// }

}
func (s *SDK) GetSenderAddr() (string, error) {
	return s.Origin()
}

// ResultSetKvImpl iterator query result KVdb
type ResultSetKvImpl struct {
	s *SDK

	index int32
}

func (r *ResultSetKvImpl) HasNext() bool {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKvIteratorHasNext),
		KeyIterIndex:        bytehelper.IntToBytes(r.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      r.s.chainId,
		TxId:         r.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KV_ITERATOR_REQUEST,
		CrossContext: r.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}

	r.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return false
	}

	has, err := bytehelper.BytesToInt(result.SysCallMessage.Payload[KeyIteratorHasNext])
	if err != nil {
		return false
	}

	if has == 0 {
		return false
	}

	return true
}

func (r *ResultSetKvImpl) Close() (bool, error) {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKvIteratorClose),
		KeyIterIndex:        bytehelper.IntToBytes(r.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      r.s.chainId,
		TxId:         r.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KV_ITERATOR_REQUEST,
		CrossContext: r.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}

	r.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return false, errors.New(result.SysCallMessage.Message)
	} else if result.SysCallMessage.Code == protocol.ContractSdkSignalResultSuccess {
		return true, nil
	}

	return true, nil
}

func (r *ResultSetKvImpl) NextRow() (*serialize.EasyCodec, error) {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKvIteratorNext),
		KeyIterIndex:        bytehelper.IntToBytes(r.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      r.s.chainId,
		TxId:         r.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KV_ITERATOR_REQUEST,
		CrossContext: r.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}

	r.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return nil, errors.New(result.SysCallMessage.Message)
	}

	key := result.SysCallMessage.Payload[KeyUserKey]
	field := result.SysCallMessage.Payload[KeyUserField]
	value := result.SysCallMessage.Payload[KeyStateValue]

	ec := serialize.NewEasyCodec()
	ec.AddString(EC_KEY_TYPE_KEY, string(key))
	ec.AddString(EC_KEY_TYPE_FIELD, string(field))
	ec.AddBytes(EC_KEY_TYPE_VALUE, value)

	return ec, nil
}

func (r *ResultSetKvImpl) Next() (string, string, []byte, error) {
	ec, err := r.NextRow()
	if err != nil {
		return "", "", nil, err
	}
	key, _ := ec.GetString(EC_KEY_TYPE_KEY)
	field, _ := ec.GetString(EC_KEY_TYPE_FIELD)
	v, _ := ec.GetBytes(EC_KEY_TYPE_VALUE)

	return key, field, v, nil
}

type KeyHistoryKvIterImpl struct {
	s *SDK

	key   string
	field string
	index int32
}

func (k *KeyHistoryKvIterImpl) HasNext() bool {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKeyHistoryIterHasNext),
		KeyIterIndex:        bytehelper.IntToBytes(k.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      k.s.chainId,
		TxId:         k.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KEY_HISTORY_ITER_REQUEST,
		CrossContext: k.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}

	k.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return false
	}

	has, err := bytehelper.BytesToInt(result.SysCallMessage.Payload[KeyIterIndex])
	if err != nil {
		return false
	}

	if Bool(has) == BoolFalse {
		return false
	}

	return true
}

func (k *KeyHistoryKvIterImpl) NextRow() (*serialize.EasyCodec, error) {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKeyHistoryIterNext),
		KeyIterIndex:        bytehelper.IntToBytes(k.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      k.s.chainId,
		TxId:         k.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KEY_HISTORY_ITER_REQUEST,
		CrossContext: k.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}
	k.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return nil, errors.New(result.SysCallMessage.Message)
	}

	/*
		| index | desc        |
		| ---   | ---         |
		| 0     | txId        |
		| 1     | blockHeight |
		| 2     | value       |
		| 3     | isDelete    |
		| 4     | timestamp   |
	*/

	txId := result.SysCallMessage.Payload[KeyTxId]
	blockHeightBytes := result.SysCallMessage.Payload[KeyBlockHeight]
	value := result.SysCallMessage.Payload[KeyStateValue]
	isDeleteBytes := result.SysCallMessage.Payload[KeyIsDelete]
	timestamp := result.SysCallMessage.Payload[KeyTimestamp]

	ec := serialize.NewEasyCodec()
	ec.AddBytes(EC_KEY_TYPE_VALUE, value)
	ec.AddString(EC_KEY_TYPE_TX_ID, string(txId))

	blockHeight, err := bytehelper.BytesToInt(blockHeightBytes)
	if err != nil {
		return nil, err
	}
	ec.AddInt32(EC_KEY_TYPE_BLOCK_HEITHT, blockHeight)

	ec.AddString(EC_KEY_TYPE_TIMESTAMP, string(timestamp))

	isDelete, err := bytehelper.BytesToInt(isDeleteBytes)
	if err != nil {
		return nil, err
	}
	ec.AddInt32(EC_KEY_TYPE_IS_DELETE, isDelete)

	ec.AddString(EC_KEY_TYPE_KEY, k.key)
	ec.AddString(EC_KEY_TYPE_FIELD, k.field)

	return ec, nil
}

func (k *KeyHistoryKvIterImpl) Close() (bool, error) {
	responseCh := make(chan *protogo.DockerVMMessage, 1)
	respNotify := func(msg *protogo.DockerVMMessage) {
		responseCh <- msg
	}

	params := map[string][]byte{
		KeyIteratorFuncName: []byte(FuncKeyHistoryIterClose),
		KeyIterIndex:        bytehelper.IntToBytes(k.index),
	}

	consumeKvIteratorReq := &protogo.DockerVMMessage{
		ChainId:      k.s.chainId,
		TxId:         k.s.txId,
		Type:         protogo.DockerVMType_CONSUME_KEY_HISTORY_ITER_REQUEST,
		CrossContext: k.s.crossCtx,
		SysCallMessage: &protogo.SysCallMessage{
			Payload: params,
		},
		Response: nil,
		Request:  nil,
	}

	k.s.sendSysCallRequestWithRespNotify(consumeKvIteratorReq, respNotify)

	result := <-responseCh

	if result.SysCallMessage.Code == protocol.ContractSdkSignalResultFail {
		return false, errors.New(result.SysCallMessage.Message)
	} else if result.SysCallMessage.Code == protocol.ContractSdkSignalResultSuccess {
		return true, nil
	}

	return true, nil
}

func (k *KeyHistoryKvIterImpl) Next() (*KeyModification, error) {
	ec, err := k.NextRow()
	if err != nil {
		return nil, err
	}

	value, _ := ec.GetBytes(EC_KEY_TYPE_VALUE)
	txId, _ := ec.GetString(EC_KEY_TYPE_TX_ID)
	blockHeight, _ := ec.GetInt32(EC_KEY_TYPE_BLOCK_HEITHT)
	isDeleteBool, _ := ec.GetInt32(EC_KEY_TYPE_IS_DELETE)
	isDelete := false
	if Bool(isDeleteBool) == BoolTrue {
		isDelete = true
	}

	timestamp, _ := ec.GetString(EC_KEY_TYPE_TIMESTAMP)

	return &KeyModification{
		Key:         k.key,
		Field:       k.field,
		Value:       value,
		TxId:        txId,
		BlockHeight: int(blockHeight),
		IsDelete:    isDelete,
		Timestamp:   timestamp,
	}, nil
}

func nameToAddr(name string) string {
	data := keccak256([]byte(name))
	bytesAddr := keccak256(data)
	return hex.EncodeToString(bytesAddr)[24:]
}

func keccak256(data []byte) []byte {
	hasher := sha3.NewLegacyKeccak256()
	hasher.Write(data)
	return hasher.Sum(nil)
}
